package nfa;

import fa.FARule;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author haoly
 * @title NFADesignTest
 * @date 2025/4/16 15:13
 * @description NFADesign测试类
 */
public class NFADesignTest {
    @Test
    void testAccepts() {
        List<FARule<Integer>> rules = Arrays.asList(
                new FARule<>(1, 'a', 1),
                new FARule<>(1, 'b', 1),
                new FARule<>(1, 'b', 2),
                new FARule<>(2, 'a', 3),
                new FARule<>(2, 'b', 3),
                new FARule<>(3, 'a', 4),
                new FARule<>(3, 'b', 4)
        );
        NFARulebook<Integer> rulebook = new NFARulebook<>(rules);

        NFADesign<Integer> nfaDesign = new NFADesign<>(1, Set.of(4), rulebook);
        assertTrue(nfaDesign.accepts("bab"));
        assertTrue(nfaDesign.accepts("bbbbb"));
        assertFalse(nfaDesign.accepts("bbabb"));
    }

    @Test
    void testAcceptsWithFreeMoves() {
        List<FARule<Integer>> rules = Arrays.asList(
                new FARule<>(1, null, 2),
                new FARule<>(1, null, 4),
                new FARule<>(2, 'a', 3),
                new FARule<>(3, 'a', 2),
                new FARule<>(4, 'a', 5),
                new FARule<>(5, 'a', 6),
                new FARule<>(6, 'a', 4)
        );
        NFARulebook<Integer> rulebook = new NFARulebook<>(rules);

        NFADesign<Integer> nfaDesign = new NFADesign<>(1, Set.of(2,4), rulebook);
        assertTrue(nfaDesign.accepts("aa"));
        assertTrue(nfaDesign.accepts("aaa"));
        assertFalse(nfaDesign.accepts("aaaaa"));
        assertTrue(nfaDesign.accepts("aaaaaa"));
    }

    @Test
    void testCurrentStates() {
        List<FARule<Integer>> rules = Arrays.asList(
                new FARule<>(1, 'a', 1),
                new FARule<>(1, 'a', 2),
                new FARule<>(1, null, 2),
                new FARule<>(2, 'b', 3),
                new FARule<>(3, 'b', 1),
                new FARule<>(3, null, 2)
        );
        NFARulebook<Integer> rulebook = new NFARulebook<>(rules);

        NFADesign<Integer> nfaDesign = new NFADesign<>(1, Set.of(3), rulebook);
        assertEquals(Set.of(1,2), nfaDesign.toNFA().getCurrentStates());
        assertEquals(Set.of(2), nfaDesign.toNFA(Set.of(2)).getCurrentStates());
        assertEquals(Set.of(2,3), nfaDesign.toNFA(Set.of(3)).getCurrentStates());

        NFA nfa = nfaDesign.toNFA(Set.of(2,3));
        assertEquals(Set.of(2,3), nfa.getCurrentStates());
        nfa.readCharacter('b');
        assertEquals(Set.of(1,2,3), nfa.getCurrentStates());

    }

    @Test
    void testCurrentStatesAndAccepting() {
        List<FARule<Integer>> rules = Arrays.asList(
                new FARule<>(1, 'a', 1),
                new FARule<>(1, 'a', 2),
                new FARule<>(1, null, 2),
                new FARule<>(2, 'b', 3),
                new FARule<>(3, 'b', 1),
                new FARule<>(3, null, 2)
        );
        NFARulebook<Integer> rulebook = new NFARulebook<>(rules);

        NFADesign<Integer> nfaDesign = new NFADesign<>(1, Set.of(3), rulebook);

        assertFalse(nfaDesign.toNFA(Set.of(1,2)).accepting());
        assertTrue(nfaDesign.toNFA(Set.of(3,2)).accepting());



    }
}
